package org.infinispan.cdi.embedded; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.HashSet; import java.util.Set; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.Dependent; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessBean; import javax.enterprise.inject.spi.ProcessProducer; import javax.enterprise.inject.spi.Producer; import javax.enterprise.util.TypeLiteral; import org.infinispan.AdvancedCache; import org.infinispan.Cache; import org.infinispan.cdi.common.util.AnyLiteral; import org.infinispan.cdi.common.util.Arrays2; import org.infinispan.cdi.common.util.BeanBuilder; import org.infinispan.cdi.common.util.Beans; import org.infinispan.cdi.common.util.ContextualLifecycle; import org.infinispan.cdi.common.util.ContextualReference; import org.infinispan.cdi.common.util.DefaultLiteral; import org.infinispan.cdi.common.util.Reflections; import org.infinispan.cdi.embedded.event.cachemanager.CacheManagerEventBridge; import org.infinispan.cdi.embedded.util.logging.EmbeddedLog; import org.infinispan.commons.logging.LogFactory; import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfiguration; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; /** * The Infinispan CDI extension for embedded caches * * @author Pete Muir * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ public class InfinispanExtensionEmbedded implements Extension { private static final String CACHE_NAME = "CDIExtensionDefaultCacheManager"; private static final EmbeddedLog LOGGER = LogFactory.getLog(InfinispanExtensionEmbedded.class, EmbeddedLog.class); private final Set<ConfigurationHolder> configurations; private volatile boolean registered = false; private final Object registerLock = new Object(); private Set<Set<Annotation>> installedEmbeddedCacheManagers = new HashSet<>(); public InfinispanExtensionEmbedded() { new ConfigurationBuilder(); // Attempt to initialize a core class this.configurations = new HashSet<>(); } @SuppressWarnings("unchecked") void observeConfigurationProducers(@Observes ProcessProducer<?, Configuration> event, BeanManager beanManager) { final ConfigureCache annotation = event.getAnnotatedMember().getAnnotation(ConfigureCache.class); String configurationName = ""; if (annotation != null) { configurationName = annotation.value(); } configurations.add(new ConfigurationHolder(event.getProducer(), configurationName, Reflections.getQualifiers(beanManager, event.getAnnotatedMember().getAnnotations()))); } public void observeEmbeddedCacheManagerBean(@Observes ProcessBean<?> processBean) { if (processBean.getBean().getTypes().contains(EmbeddedCacheManager.class)) { installedEmbeddedCacheManagers.add(processBean.getBean().getQualifiers()); } } @SuppressWarnings("unchecked") <T, X> void registerBeans(@Observes AfterBeanDiscovery event, final BeanManager beanManager) { if (beanManager.getBeans(Configuration.class).isEmpty()) { LOGGER.addDefaultEmbeddedConfiguration(); final Configuration defaultConfiguration = new ConfigurationBuilder().build(); // Must be added after AdvancedCache producer registration - see also AdvancedCacheProducer.getDefaultAdvancedCache() configurations.add(new ConfigurationHolder(defaultConfiguration, "", defaultQualifiers())); event.addBean(createDefaultEmbeddedConfigurationBean(beanManager, defaultConfiguration)); } for (final ConfigurationHolder holder : configurations) { // register a AdvancedCache producer for each configuration Bean<?> advancedCacheBean = new BeanBuilder(beanManager) .readFromType(beanManager.createAnnotatedType(AdvancedCache.class)) .qualifiers(Beans.buildQualifiers(holder.getQualifiers())) .addType(new TypeLiteral<AdvancedCache<T, X>>() {}.getType()) .addType(new TypeLiteral<Cache<T, X>>() {}.getType()) .passivationCapable(true) .id(InfinispanExtensionEmbedded.class.getSimpleName() + "#" + AdvancedCache.class.getSimpleName() + "#" + Cache.class.getSimpleName()) .beanLifecycle(new ContextualLifecycle<AdvancedCache<?, ?>>() { @Override public AdvancedCache<?, ?> create(Bean<AdvancedCache<?, ?>> bean, CreationalContext<AdvancedCache<?, ?>> creationalContext) { return new ContextualReference<AdvancedCacheProducer>(beanManager, AdvancedCacheProducer.class).create(Reflections.cast(creationalContext)).get().getAdvancedCache(holder.getName(), holder.getQualifiers()); } }).create(); event.addBean(advancedCacheBean); } if (beanManager.getBeans(EmbeddedCacheManager.class).isEmpty()) { LOGGER.addDefaultEmbeddedCacheManager(); event.addBean(createDefaultEmbeddedCacheManagerBean(beanManager)); } } <K, V> void registerInputCacheCustomBean(@Observes AfterBeanDiscovery event, BeanManager beanManager) { @SuppressWarnings("serial") TypeLiteral<Cache<K, V>> typeLiteral = new TypeLiteral<Cache<K, V>>() {}; event.addBean(new BeanBuilder<Cache<K, V>>(beanManager) .readFromType(beanManager.createAnnotatedType(typeLiteral.getRawType())) .addType(typeLiteral.getType()).qualifiers(new InputLiteral()) .beanLifecycle(new ContextualLifecycle<Cache<K, V>>() { @Override public Cache<K, V> create(Bean<Cache<K, V>> bean, CreationalContext<Cache<K, V>> creationalContext) { return ContextInputCache.get(); } }).create()); @SuppressWarnings("serial") TypeLiteral<Collection<K>> typeLiteralKeys = new TypeLiteral<Collection<K>>() {}; event.addBean(new BeanBuilder<Collection<K>>(beanManager) .readFromType(beanManager.createAnnotatedType(typeLiteralKeys.getRawType())) .addType(typeLiteralKeys.getType()).qualifiers(new InputLiteral()) .beanLifecycle(new ContextualLifecycle<Collection<K>>() { @Override public Collection<K> create(Bean<Collection<K>> bean, CreationalContext<Collection<K>> creationalContext) { return ContextInputCache.getKeys(); } }).create()); } public Set<InstalledCacheManager> getInstalledEmbeddedCacheManagers(BeanManager beanManager) { Set<InstalledCacheManager> installedCacheManagers = new HashSet<>(); for (Set<Annotation> qualifiers : installedEmbeddedCacheManagers) { Bean<?> b = beanManager.resolve(beanManager.getBeans(EmbeddedCacheManager.class, qualifiers.toArray(Reflections.EMPTY_ANNOTATION_ARRAY))); EmbeddedCacheManager cm = (EmbeddedCacheManager) beanManager.getReference(b, EmbeddedCacheManager.class, beanManager.createCreationalContext(b)); installedCacheManagers.add(new InstalledCacheManager(cm, qualifiers.contains(DefaultLiteral.INSTANCE))); } return installedCacheManagers; } public void registerCacheConfigurations(CacheManagerEventBridge eventBridge, Instance<EmbeddedCacheManager> cacheManagers, BeanManager beanManager) { if (!registered) { synchronized (registerLock) { if (!registered) { final CreationalContext<Configuration> ctx = beanManager.createCreationalContext(null); final EmbeddedCacheManager defaultCacheManager = cacheManagers.select(DefaultLiteral.INSTANCE).get(); for (ConfigurationHolder holder : configurations) { final String cacheName = holder.getName(); final Configuration cacheConfiguration = holder.getConfiguration(ctx); final Set<Annotation> cacheQualifiers = holder.getQualifiers(); // if a specific cache manager is defined for this cache we use it final Instance<EmbeddedCacheManager> specificCacheManager = cacheManagers.select(cacheQualifiers.toArray(new Annotation[cacheQualifiers.size()])); final EmbeddedCacheManager cacheManager = specificCacheManager.isUnsatisfied() ? defaultCacheManager : specificCacheManager.get(); // the default configuration is registered by the default cache manager producer if (!cacheName.trim().isEmpty()) { if (cacheConfiguration != null) { cacheManager.defineConfiguration(cacheName, cacheConfiguration); LOGGER.cacheConfigurationDefined(cacheName, cacheManager); } else if (!cacheManager.getCacheNames().contains(cacheName)) { cacheManager.defineConfiguration(cacheName, cacheManager.getDefaultCacheConfiguration()); LOGGER.cacheConfigurationDefined(cacheName, cacheManager); } } // register cache manager observers eventBridge.registerObservers(cacheQualifiers, cacheName, cacheManager); } // only set registered to true at the end to keep other threads waiting until we have finished registration registered = true; } } } } /** * The default embedded cache configuration can be overridden by creating a producer which * produces the new default configuration. The configuration produced must have the scope * {@linkplain javax.enterprise.context.Dependent Dependent} and the * {@linkplain javax.enterprise.inject.Default Default} qualifier. * * @param beanManager * @return a custom bean */ private Bean<Configuration> createDefaultEmbeddedConfigurationBean(BeanManager beanManager, final Configuration configuration) { return new BeanBuilder<Configuration>(beanManager).beanClass(InfinispanExtensionEmbedded.class) .addTypes(Object.class, Configuration.class) .scope(Dependent.class) .qualifiers(defaultQualifiers()) .passivationCapable(true) .id(InfinispanExtensionEmbedded.class.getSimpleName() + "#" + Configuration.class.getSimpleName()) .beanLifecycle(new ContextualLifecycle<Configuration>() { @Override public Configuration create(Bean<Configuration> bean, CreationalContext<Configuration> creationalContext) { return configuration; } }).create(); } /** * The default cache manager is an instance of {@link DefaultCacheManager} initialized with the * default configuration (either produced by * {@link #createDefaultEmbeddedConfigurationBean(BeanManager)} or provided by user). The default * cache manager can be overridden by creating a producer which produces the new default cache * manager. The cache manager produced must have the scope {@link ApplicationScoped} and the * {@linkplain javax.enterprise.inject.Default Default} qualifier. * * @param beanManager * @return a custom bean */ private Bean<EmbeddedCacheManager> createDefaultEmbeddedCacheManagerBean(BeanManager beanManager) { return new BeanBuilder<EmbeddedCacheManager>(beanManager).beanClass(InfinispanExtensionEmbedded.class) .addTypes(Object.class, EmbeddedCacheManager.class) .scope(ApplicationScoped.class) .qualifiers(defaultQualifiers()) .passivationCapable(true) .id(InfinispanExtensionEmbedded.class.getSimpleName() + "#" + EmbeddedCacheManager.class.getSimpleName()) .beanLifecycle(new ContextualLifecycle<EmbeddedCacheManager>() { @Override public EmbeddedCacheManager create(Bean<EmbeddedCacheManager> bean, CreationalContext<EmbeddedCacheManager> creationalContext) { GlobalConfiguration globalConfiguration = new GlobalConfigurationBuilder().globalJmxStatistics() .cacheManagerName(CACHE_NAME).build(); @SuppressWarnings("unchecked") Bean<Configuration> configurationBean = (Bean<Configuration>) beanManager .resolve(beanManager.getBeans(Configuration.class)); Configuration defaultConfiguration = (Configuration) beanManager.getReference(configurationBean, Configuration.class, beanManager.createCreationalContext(configurationBean)); return new DefaultCacheManager(globalConfiguration, defaultConfiguration); } @Override public void destroy(Bean<EmbeddedCacheManager> bean, EmbeddedCacheManager instance, CreationalContext<EmbeddedCacheManager> creationalContext) { instance.stop(); } }).create(); } private Set<Annotation> defaultQualifiers() { return Arrays2.asSet(DefaultLiteral.INSTANCE, AnyLiteral.INSTANCE); } static class ConfigurationHolder { private final Producer<Configuration> producer; private final Set<Annotation> qualifiers; private final String name; private final Configuration configuration; ConfigurationHolder(Producer<Configuration> producer, String name, Set<Annotation> qualifiers) { this(producer, qualifiers, name, null); } ConfigurationHolder(Configuration configuration, String name, Set<Annotation> qualifiers) { this(null, qualifiers, name, configuration); } private ConfigurationHolder(Producer<Configuration> producer, Set<Annotation> qualifiers, String name, Configuration configuration) { this.producer = producer; this.qualifiers = qualifiers; this.name = name; this.configuration = configuration; } public Producer<Configuration> getProducer() { return producer; } public String getName() { return name; } public Set<Annotation> getQualifiers() { return qualifiers; } Configuration getConfiguration(CreationalContext<Configuration> ctx) { return configuration != null ? configuration : producer.produce(ctx); } } public static class InstalledCacheManager { final EmbeddedCacheManager cacheManager; final boolean isDefault; InstalledCacheManager(EmbeddedCacheManager cacheManager, boolean aDefault) { this.cacheManager = cacheManager; isDefault = aDefault; } public EmbeddedCacheManager getCacheManager() { return cacheManager; } public boolean isDefault() { return isDefault; } } }